package migrate

import (
	"fmt"
	appsv1 "k8s.io/api/apps/v1"
	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/json"
	"reflect"
	"strings"
)

// CombineResourceKey 将命名空间、资源类型、资源名称组合为用冒号分隔的资源标识键
// 格式：namespace:kind:name
func CombineResourceKey(namespace, kind, name string) string {
	return strings.Join([]string{namespace, kind, name}, ":")
}

// ParseResourceKey 解析资源标识键为命名空间、资源类型、资源名称
// key 格式必须为 "namespace:kind:name"，否则返回错误
func ParseResourceKey(key string) (namespace, kind, name string, err error) {
	parts := strings.Split(key, ":")

	// 校验分割后的部分数量是否为3
	if len(parts) != 3 {
		return "", "", "", fmt.Errorf("资源键格式错误，需满足 'namespace:kind:name'，实际分割为 %d 部分", len(parts))
	}

	// 校验各部分非空（可根据需求决定是否保留）
	fields := []struct {
		value string
		name  string
	}{
		{parts[0], "命名空间(namespace)"},
		{parts[1], "资源类型(kind)"},
		{parts[2], "资源名称(name)"},
	}
	for _, field := range fields {
		if field.value == "" {
			return "", "", "", fmt.Errorf("资源键中 %s 不能为空", field.name)
		}
	}

	return parts[0], parts[1], parts[2], nil
}

// 统一更新 metadata.namespace
func reflectSetNamespace(obj interface{}, newNamespace string) {
	if metaObj, ok := obj.(metav1.Object); ok {
		metaObj.SetNamespace(newNamespace)
	}
}

// 针对 Deployment / StatefulSet / Job / CronJob 等模板对象的 Namespace 修复
func updateTemplateNamespace(obj interface{}, newNamespace string) {
	switch o := obj.(type) {
	case *appsv1.Deployment:
		if o.Spec.Template.ObjectMeta.Namespace != "" {
			o.Spec.Template.ObjectMeta.Namespace = newNamespace
		}
	case *appsv1.StatefulSet:
		if o.Spec.Template.ObjectMeta.Namespace != "" {
			o.Spec.Template.ObjectMeta.Namespace = newNamespace
		}
	case *appsv1.DaemonSet:
		if o.Spec.Template.ObjectMeta.Namespace != "" {
			o.Spec.Template.ObjectMeta.Namespace = newNamespace
		}
	case *batchv1.Job:
		if o.Spec.Template.ObjectMeta.Namespace != "" {
			o.Spec.Template.ObjectMeta.Namespace = newNamespace
		}
	case *batchv1.CronJob:
		if o.Spec.JobTemplate.Spec.Template.ObjectMeta.Namespace != "" {
			o.Spec.JobTemplate.Spec.Template.ObjectMeta.Namespace = newNamespace
		}
	case *batchv1beta1.CronJob:
		if o.Spec.JobTemplate.Spec.Template.ObjectMeta.Namespace != "" {
			o.Spec.JobTemplate.Spec.Template.ObjectMeta.Namespace = newNamespace
		}
	}
}

// updateNonWorkloadNamespace 处理非 workload 类型资源的命名空间替换逻辑。
func updateNonWorkloadNamespace(obj interface{}, newNamespace string) {
	switch o := obj.(type) {

	// ----- 核心命名空间资源 -----
	case *corev1.Service:
		reflectSetNamespace(o, newNamespace)
	case *networkingv1.Ingress:
		reflectSetNamespace(o, newNamespace)
	case *corev1.ConfigMap:
		reflectSetNamespace(o, newNamespace)
	case *corev1.Secret:
		reflectSetNamespace(o, newNamespace)
	case *corev1.PersistentVolumeClaim:
		reflectSetNamespace(o, newNamespace)
	case *corev1.ServiceAccount:
		reflectSetNamespace(o, newNamespace)

	// ----- RBAC 资源 -----
	case *rbacv1.Role:
		reflectSetNamespace(o, newNamespace)
	case *rbacv1.RoleBinding:
		reflectSetNamespace(o, newNamespace)
		// 修正 RoleBinding.Subjects 中的 ServiceAccount 命名空间
		for i := range o.Subjects {
			if o.Subjects[i].Kind == "ServiceAccount" && o.Subjects[i].Namespace != "" {
				o.Subjects[i].Namespace = newNamespace
			}
		}
	case *rbacv1.ClusterRoleBinding:
		for i := range o.Subjects {
			if o.Subjects[i].Kind == "ServiceAccount" {
				o.Subjects[i].Namespace = newNamespace
			}
		}
		// ----- 存储相关 -----
	case *corev1.PersistentVolume:
		// PV 是 cluster-scoped 资源，不改 metadata.namespace，
		// 但如果它绑定了某个 PVC，需要更新 claimRef.namespace。
		if o.Spec.ClaimRef != nil && o.Spec.ClaimRef.Namespace != "" {
			o.Spec.ClaimRef.Namespace = newNamespace
		}

	// ----- 集群级资源（跳过） -----
	case *rbacv1.ClusterRole:
		// 集群级资源不属于命名空间，跳过
		return
	}
}

// UnmarshalTo 负责安全地将 JSON 数据反序列化为指定类型，并进行基本字段校验
func UnmarshalTo(data []byte, out runtime.Object) error {
	// 使用 json.Unmarshal 更稳妥，先反序列化到 out
	if err := json.Unmarshal(data, out); err != nil {
		return fmt.Errorf("failed to unmarshal JSON to object: %w", err)
	}

	// 从对象提取 GVK（Group/Version/Kind）并赋值给 runtime.Object
	// 避免某些资源 decode 后没有 GVK 的问题
	if accessor, ok := out.(interface{ GetObjectKind() schema.ObjectKind }); ok {
		gvk := out.GetObjectKind().GroupVersionKind()
		if gvk.Empty() {
			// 手动解析 data 中的 apiVersion/kind
			tmp := struct {
				APIVersion string `json:"apiVersion"`
				Kind       string `json:"kind"`
			}{}
			if err := json.Unmarshal(data, &tmp); err == nil && tmp.APIVersion != "" && tmp.Kind != "" {
				gv, err := schema.ParseGroupVersion(tmp.APIVersion)
				if err == nil {
					accessor.GetObjectKind().SetGroupVersionKind(gv.WithKind(tmp.Kind))
				}
			}
		}
	}

	// 做关键字段检查（metadata.name 和 apiVersion/kind）
	// 这部分可以让错误尽早在本地发现，而不是等到 Create 阶段
	meta := reflect.ValueOf(out).Elem().FieldByName("ObjectMeta")
	if meta.IsValid() {
		nameField := meta.FieldByName("Name")
		if nameField.IsValid() && nameField.String() == "" {
			return fmt.Errorf("missing metadata.name in object %T", out)
		}
	}

	return nil
}
